﻿using OWL.Rendering.HRP;
using System;
using System.Collections.Generic;
using UnityEngine;
using Yoozoo.Gameplay.RTS;
using Yoozoo.Managers.ResourceManagerV2.Runtime;

namespace Yoozoo.Mars.Got.SpriteAnim
{

    public class SpriteParticleSystemManager : MonoBehaviour
    {
        private const int INITIAL_SPEED = 1;
        public static int INITAL_PARTICLE_NUM = 2000;  //1个ps
        private const string particlePath = "Assets/ResourcesAssets/Prefabs/Character/2DAnimation/ParticleSystemNode.prefab";
        private const string shaderString = "Sprites/Default";
        private const string colorName = "_EmissionColor";

        private List<List<CreatorVisitor>> creatorList;
        private Dictionary<string, int> spriteNameDictionary = new Dictionary<string, int>();
        private Dictionary<string, Material> spriteMaterialDictionary = new Dictionary<string, Material>();
        private Dictionary<string, int> animationNameDictionary = new Dictionary<string, int>();

        [HideInInspector]
        private GameObject prefab;
        [HideInInspector]
        private GameObject particleRoot;
        [HideInInspector]
        private GameObject spriteGoRoot;
        [HideInInspector]
        public SpriteResourceManager resourceManager;
        private int indexSprite = 0;

        private static int visitKey = -1;
        private Interface_AnimSpriteSupportTemplate interface_direct;
        private BaseSpriteCache spriteCache;
        private ResLoader _resLoader;

        private bool enabled = true;
        
        public List<List<CreatorVisitor>> GetCreators()
        {
            return creatorList;
        }
        public void InitManager(Interface_AnimSpriteSupportTemplate m_interface_direct)
        {
            enabled = true;
            _resLoader = ResLoader.Alloc();
            this.spriteCache = new BaseSpriteCache();
            interface_direct = m_interface_direct;
            creatorList = new List<List<CreatorVisitor>>();
            resourceManager = new SpriteResourceManager(spriteCache);
            //if (Debug.unityLogger.logEnabled) { Debug.Log("SpriteParticleSystemManager particlePath >>" + particlePath); }
            particleRoot = new GameObject("particleSystem_list");
            particleRoot.transform.parent = this.transform;
            if (visitKey == -1)
            {
                visitKey = UnityEngine.Random.Range(0, int.MaxValue);
            }
            prefab = new GameObject("ParticleSystemBase");
            prefab.transform.parent = this.transform;
        }
        public void Load(Action callback = null)
        {
            /*_resLoader.Add2Load(particlePath, (success, name, asset) =>
            {
                prefab = UnityEngine.Object.Instantiate((UnityEngine.Object)asset) as GameObject;
                prefab.name = "ParticleSystemBase";
                prefab.transform.parent = this.transform;
                for (int i = 0; i < onSpriteParticlePrefabReady.Count; i++)
                {
                    onSpriteParticlePrefabReady[i]();
                }
                onSpriteParticlePrefabReady.Clear();
                if (callback != null)
                    callback();
            }).Load();*/
     
            for (int i = 0; i < onSpriteParticlePrefabReady.Count; i++)
            {
                onSpriteParticlePrefabReady[i]();
            }
            onSpriteParticlePrefabReady.Clear();
            if (callback != null)
                callback();
        }
        
        public AnimSprite[] CreateAnimSprites(AnimSpriteCreator creator, int count, string spriteName = null)
        {
            if (creator == null)
            {
                if (Debug.unityLogger.logEnabled) { Debug.LogError("禁止传入空的Creator进行创建粒子操作"); }
                return null;
            }
            if (spriteName == null)
            {
                spriteName = creator.AltasName;
            }
            AnimSprite[] sprites = ((CreatorVisitor)creator).AddParticleFromUnitPars_Visit(spriteName,count, visitKey);

            return sprites;
        }

        public AnimSprite CreateAnimSprite(AnimSpriteCreator creator)
        {
            if (creator == null)
            {
                //if (Debug.unityLogger.logEnabled) { Debug.LogError("禁止传入空的Creator进行创建粒子操作"); }
                return null;
            }
            AnimSprite sprite = ((CreatorVisitor)creator).AddSingleParticleFromUnitPars_Visit(creator.AltasName,visitKey);

            return sprite;
        }

        public int RemoveSprite(AnimSprite spriteGo)
        {
            int changedSpriteIndex = -1;
            CreatorVisitor visitor = ((CreatorVisitor)spriteGo.ptSystemNode);
            if (visitor != null)
            {
                changedSpriteIndex = visitor.ForceRemoveParticle_Visit(spriteGo, visitKey);
                
                ((SpriteVisitor)spriteGo).Dispose_v();
            }

            return changedSpriteIndex;
            //spriteGo = null;
        }

        public void RemoveSprites(List<AnimSprite> spriteGo)
        {
            CreatorVisitor visitor = ((CreatorVisitor)spriteGo[0].ptSystemNode);
            visitor.ForceRemoveParticle_Visit(spriteGo, visitKey);

            for (int index = 0; index < spriteGo.Count; index++)
            {
                ((SpriteVisitor)spriteGo[index]).Dispose_v();
            }
        }

        private void OnDestroy()
        {
            DrawMeshInstInfo.ClearCache();
            Dispose();
        }
        public void Dispose()
        {
            if (creatorList != null)
            {
                for (int index = 0; index < creatorList.Count; index++)
                {
                    for (int indexIn = 0; indexIn < creatorList[index].Count; indexIn++)
                    {
                        creatorList[index][indexIn].Dispose_Visit();
                    }
                }

                creatorList.Clear();
                creatorList = null;

                spriteNameDictionary.Clear();
                creatorList = null;

                animationNameDictionary.Clear();
                animationNameDictionary = null;

                GameObject.Destroy(particleRoot);
                particleRoot = null;
                GameObject.Destroy(spriteGoRoot);
                spriteGoRoot = null;
                GameObject.Destroy(prefab);
                prefab = null;

                spriteCache.Clean();
                spriteCache = null;

                resourceManager.Dispose();
                resourceManager = null;


                _resLoader.Recycle2Cache();
                _resLoader = null;
            }
          
        }
        public bool hasSpriteCreatorByName(string creatorName)
        {
            for (int index = 0; index < creatorList.Count; index++)
            {
                for (int indexIn = 0; indexIn < creatorList[index].Count; indexIn++)
                {
                    if (creatorList[index][indexIn].CreatorName == creatorName)
                    {

                        return true;
                    }
                }
            }
            return false;
        }

        public void GetSpriteCreatorByName(string creatorName, Action<AnimSpriteCreator> callback)
        {
            for (int index = 0; index < creatorList.Count; index++)
            {
                for (int indexIn = 0; indexIn < creatorList[index].Count; indexIn++)
                {
                    if (creatorList[index][indexIn].CreatorName == creatorName)
                    {
                        if (callback != null)
                        {
                            callback(creatorList[index][indexIn]);
                            return;
                        }

                    }
                }
            }
            if (callback != null)
            {
                callback(null);
            }
        }

        //直接返回的
        public AnimSpriteCreator GetSpriteCreatorByName(string creatorName)
        {
            for (int index = 0; index < creatorList.Count; index++)
            {
                for (int indexIn = 0; indexIn < creatorList[index].Count; indexIn++)
                {
                    if (creatorList[index][indexIn].CreatorName == creatorName)
                    {
                        return creatorList[index][indexIn];
                    }
                }
            }
            return null;
        }

        public AnimSpriteCreator GetSpriteCreatorByOriginNameAndTeam(string originName, int team)
        {
            for (int index = 0; index < creatorList.Count; index++)
            {
                for (int indexIn = 0; indexIn < creatorList[index].Count; indexIn++)
                {
                    var visitor = creatorList[index][indexIn];
                    if (visitor.CreatorOriginName == originName && visitor.CreatorTeam == team)
                    {
                        return creatorList[index][indexIn];
                    }
                }
            }
            return GetSpriteCreatorByName(originName +  team);
        }

        //改变当前精灵的动作
        public void PlayActionAnimation(AnimSprite spriteGo, SPRITE_ACTION_ENUM actionAnimation)
        {
            if (spriteGo == null)
            {
                if (Debug.unityLogger.logEnabled) { Debug.LogError("当前精灵为空，请检查"); }
                return;
            }
            spriteGo.action = actionAnimation;
            spriteGo.changeAction = true;
        }

        private List<Action>  onSpriteParticlePrefabReady = new List<Action>();

        public void LoadSprite(string altasName,Action loadSucess)
        {
            if (!spriteCache.HasAdd2LoadSprite(altasName))
            {
                spriteCache.Add2Load(altasName);
                
                spriteCache.LoadSoldierAtlas(altasName, () =>
                {
                    loadSucess?.Invoke();
                });
            }
            else
            {
                spriteCache.AddLoadFinishAction(altasName, loadSucess);
            }
        }

        public bool HasAdd2LoadSprite(string altasName)
        {
            return spriteCache.HasAdd2LoadSprite(altasName);
        }
        public void CreateSpriteCreator(string altasName,string creatorName, string shaderName = shaderString, Action<AnimSpriteCreator> callback = null,int layer = 0,float offset = 0)
        {
            if (!spriteCache.hasCache(altasName))
            {
                //if (Debug.unityLogger.logEnabled) { Debug.Log("没有找到altasName = " + altasName); }
                spriteCache.LoadSoldierAtlas(altasName, () =>
                {
                    //if (Debug.unityLogger.logEnabled) { Debug.Log("altasName = " + altasName + "加载成功   创建spriteCreator..."); }
                    CreateSpriteCreator(altasName, creatorName, shaderName, callback, layer, offset);
                });
                return;
            }
            if (prefab == null)
            {
                onSpriteParticlePrefabReady.Add(() =>
                {
                    CreateSpriteCreator(altasName, creatorName, shaderName, callback, layer, offset);
                });
                return;
            }
            if (hasSpriteCreatorByName(creatorName))
            {   
               // if (Debug.unityLogger.logEnabled) { Debug.LogWarning("已经创建过同名creator，请检查"); }
                GetSpriteCreatorByName(creatorName, (AnimSpriteCreator creator) => {
                    if (callback != null)
                    {
                        callback.Invoke(creator);
                    }
                });
            }
            else
            {
                Material creatorMaterial = null;
                if (!spriteNameDictionary.ContainsKey(altasName))
                {
                    spriteNameDictionary.Add(altasName, indexSprite);
                    creatorList.Add(new List<CreatorVisitor>());
                    if (altasName == "shadow")
                    {
                        creatorMaterial = RTSSpriteManager.Inst.shadowMat;
                    }
                    else
                    {
                        creatorMaterial = interface_direct.GetMaterial(altasName);
                    }
                    spriteMaterialDictionary.Add(altasName, creatorMaterial);
                    indexSprite++;
                }

                creatorMaterial = spriteMaterialDictionary[altasName];
                int altasIndex = spriteNameDictionary[altasName];
                //local_sprite_index = spriteNameDictionary[altasName];
                if (creatorMaterial != null)
                {
                    creatorMaterial.SetColor(colorName, new Color(0.5f, 0.5f, 0.5f, 1f));
                }

                GameObject go = (GameObject)Instantiate(prefab);
                go.transform.position = new Vector3(0, offset,0);
                go.transform.parent = particleRoot.transform;
                go.name = creatorName;
                go.layer = layer;
                CreatorVisitor newCreator = new CreatorVisitor(interface_direct, go, altasName, creatorMaterial, INITAL_PARTICLE_NUM, visitKey, spriteCache);
                creatorList[altasIndex].Add(newCreator);
                newCreator.SetCameraRotation(cameraRotation);
                //if (Debug.unityLogger.logEnabled) { Debug.Log("  创建spriteCreator... 完毕  creatorName=" + creatorName); }
                if (callback != null)
                {
                    //if (Debug.unityLogger.logEnabled) { Debug.Log("  创建spriteCreator... 完毕。。。回调creatorName = " + creatorName); }
                    callback(newCreator);
                }
            }
        }

        public void SetRendererPropertyBlock(AnimSpriteCreator creator, MaterialPropertyBlock block)
        {
            ((CreatorVisitor)creator).SetPropertyBlock_Visit(block, visitKey);
        }

        public void GetRendererPropertyBlock(AnimSpriteCreator creator, MaterialPropertyBlock block)
        {
            ((CreatorVisitor)creator).GetPropertyBlock_Visit(block, visitKey);
        }
        public void SetRendererPriority(AnimSpriteCreator creator,int priority)
        {
            ((CreatorVisitor)creator).SetRendererPriority(priority, visitKey);
        }
        //设置绘制优先级，优先级数越大，优先级越高
        public void SetRanderPriority(AnimSpriteCreator creator, float priority)
        {
            ((CreatorVisitor)creator).SetRanderPriority_Visit(priority, visitKey);
        }

        public void SetAllParticleEnable(bool enable)
        {
            foreach (var list in creatorList)
            {
                foreach (var visitor in list)
                {
                    visitor.SetRenderEnable(enable);
                }
            }
            enabled = enable;
        }
        private CreatorVisitor creator;
        private int creatorListNum;
        private int creatorListCount;


        void Update()
        {
            if (!SystemInfo.supportsInstancing)
            {
                return;
            }

            if (!enabled)
            {
                return;
            }
            if (creatorList == null)
            {
                if (Debug.isDebugBuild)
                {
                    Debug.Log("SpriteParticleSystemManager 报错了。。。。");
                }
                return;
            }
#if UWA
            foreach (var pair in spriteNameDictionary)
            {
                string spriteName = pair.Key;
                int index = pair.Value;
                var creators = creatorList[index];
                int total = 0;
                foreach (var visitor in creators)
                {
                    total += visitor.ParticleCount;
                }
                UWAEngine.LogValue(spriteName, total);
            }
#endif
            //execute jobs
            for (int index = 0; index < creatorListNum; index++)
            {
                var creators = creatorList[index];
                creatorListCount = creators.Count;
                for (int indexIn = 0; indexIn < creatorListCount; indexIn++)
                {
                    creator = creators[indexIn];
                    creator.Schedule();
                }
            }
            
            
            var manager = HRenderSettingsManager.Get();
            var dmidata = manager.GetData<HDrawMeshInstData>();
            if (dmidata == null)
            {
                dmidata = new HDrawMeshInstData();
                manager.AddData(dmidata);
            }
            var instances = dmidata.m_instInfos;
            DrawMeshInstInfo.ClearList(instances);

            creator = null;
            //complete jobs
            creatorListNum = creatorList.Count;
            for (int index = 0; index < creatorListNum; index++)
            {
                var creators = creatorList[index];
                creatorListCount = creators.Count;
                for (int indexIn = 0; indexIn < creatorListCount; indexIn++)
                {
                    creator = creators[indexIn];
                    creator.CompleteJob_Visit(visitKey);
                }
            }
            /*/ swap draw calls, to make same subshader together
            var newRenders = instances.Count;
            for (int i = 1, j = newRenders - 2; i < j; i+=2, j-=2)
            {
                var t = instances[i];
                instances[i] = instances[j];
                instances[j] = t;
            } //*/
        }

        public int GetSpriteCount()
        {
            int count = 0;
        
            foreach (var visitors in creatorList)
            {
                foreach (var visitor in visitors)
                {
                    count += visitor.ParticleCount;
                }
            }
        
            return count;
        }

        public List<List<CreatorVisitor>> GetAllVisitor()
        {
            return creatorList;
        }

        public void ExpendCreator()
        {
            foreach (var visitors in creatorList)
            {
                foreach (var visitor in visitors)
                {
                    visitor.ExpendArrays();
                }
            }
        }

        public void SetCreatorEnabled(bool enabled)
        {
            foreach (var visitors in creatorList)
            {
                foreach (var visitor in visitors)
                {
                    visitor.SetEnabled(enabled);
                }
            }
        }

        private float cameraRotation = 45;

        public void SetCameraRotation(float cameraRotation)
        {
            foreach (var visitors in creatorList)
            {
                foreach (var visitor in visitors)
                {
                    visitor.SetCameraRotation(cameraRotation);
                }
            }

            this.cameraRotation = cameraRotation;
        }
    }
}
